package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.MessageException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.BizNodeDto;
import com.uinnova.product.eam.model.vo.CiRltQueryVo;
import com.uinnova.product.eam.model.vo.CiSimpleInfoVo;
import com.uinnova.product.eam.model.vo.PanoramaCiInfoVo;
import com.uinnova.product.eam.service.IBusinessPanoramaSvc;
import com.uinnova.product.eam.service.ICIRltSwitchSvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.asset.BmConfigSvc;
import com.uinnova.product.eam.service.es.AppSquareConfigDao;
import com.uinnova.product.eam.service.es.IamsESCmdbCommDesignSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.comm.model.rlt.CCcCiRlt;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CiGroupPage;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.db.eam.ESDiagramDao;
import com.uino.api.client.cmdb.ICIClassApiSvc;
import com.uino.api.client.cmdb.IDataSetApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.base.dataset.OperateType;
import com.uino.bean.cmdb.base.dataset.relation.RelationRuleLine;
import com.uino.bean.cmdb.base.dataset.relation.RelationRuleNode;
import com.uino.bean.cmdb.query.ESAttrBean;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.service.cmdb.microservice.IRltClassSvc;
import com.uino.util.sys.SysUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务架构全景相关服务
 *
 * @author changhu
 * @version 2021-10-09
 */
@Service
public class BusinessPanoramaSvcImpl implements IBusinessPanoramaSvc {

    @Autowired
    private ICIRltSwitchSvc ciRltSvc;
    @Autowired
    private IRltClassSvc rltClassSvc;
    @Autowired
    private ESDiagramDao esDiagramDao;
    @Autowired
    IamsESCmdbCommDesignSvc commSvc;
    @Autowired
    private ICISwitchSvc iciSwitchSvc;
    @Autowired
    private ICIClassApiSvc iciClassApiSvc;
    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;
    @Resource
    private BmConfigSvc bmConfigSvc;
    @Resource
    private AppSquareConfigDao squareConfigDao;
    @Resource
    private IDataSetApiSvc dataSetApiSvc;
    private static final String BUS_TREE_NAME = "EBPM-业务能力";
    private static final String ATTR_ABILITY = "所属能力";
    private static final String ATTR_NAME = "名称";
    private static final String ATTR_LEVEL = "层级";

    private CcCiInfo getCiByClassNameAndAttrs(String ciClassName, List<ESAttrBean> attrBeans, LibType libType) {
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassNameEqual(ciClassName);
        List<CcCiClassInfo> ccCiClassInfos = iciClassApiSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(ccCiClassInfos)) {
            return null;
        }
        Long ciClassId = ccCiClassInfos.get(0).getCiClass().getId();
        ESCISearchBean esciSearchBean = new ESCISearchBean();
        esciSearchBean.setClassIds(Arrays.asList(ciClassId));
        esciSearchBean.setAndAttrs(attrBeans);
        CiGroupPage ciGroupPage = iciSwitchSvc.queryPageBySearchBean(esciSearchBean, false, libType);
        if(CollectionUtils.isEmpty(ciGroupPage.getData())){
            return null;
        }
        return ciGroupPage.getData().get(0);
    }

    private List<CcCiRltInfo> getRltByRltClassId(List<Long> rltClassIds, List<Long> sourceCiIds, List<Long> targetCiIds, LibType libType){
        ESRltSearchBean rltSearchBean = new ESRltSearchBean();
        CCcCiRlt cCcCiRlt = new CCcCiRlt();
        if(CollectionUtils.isNotEmpty(sourceCiIds)){
            cCcCiRlt.setSourceCiIds(sourceCiIds.toArray(new Long[0]));
        }
        if(CollectionUtils.isNotEmpty(targetCiIds)){
            cCcCiRlt.setTargetCiIds(targetCiIds.toArray(new Long[0]));
        }
        rltSearchBean.setCdt(cCcCiRlt);
        rltSearchBean.setRltClassIds(rltClassIds);
        rltSearchBean.setPageSize(1000);
        Page<CcCiRltInfo> ccCiRltInfoPage = ciRltSvc.searchRltByBean(rltSearchBean, libType);
        return ccCiRltInfoPage.getData();
    }

    @Override
    public Map<String, Object> getBusMatrixView(LibType libType) {
        String ebpmSY = "EBPM-使用",ebpmCZC = "EBPM-操作-C",ebpmCZR = "EBPM-操作-R",ebpmCZU = "EBPM-操作-U",ebpmYWZJ = "EBPM-业务组件";
        ESAttrBean attrBean1 = new ESAttrBean();
        attrBean1.setKey("名称");
        attrBean1.setValue("检查/计算费用组件");
        attrBean1.setOptType(1);
        CcCiInfo ciInfo = this.getCiByClassNameAndAttrs(ebpmYWZJ, Arrays.asList(attrBean1), libType);
        if(ciInfo==null){
            return new HashMap<>(0);
        }
        CCcCiClass rltClassCdt = new CCcCiClass();
        rltClassCdt.setClassNames(new String[]{ebpmSY,ebpmCZC,ebpmCZR,ebpmCZU});
        List<CcCiClassInfo> rltClass = rltClassSvc.getRltClassByCdt(rltClassCdt);
        Map<String, CcCiClassInfo> rltClassMap = new HashMap<>();
        for(CcCiClassInfo ccCiClassInfo : rltClass){
            rltClassMap.put(ccCiClassInfo.getCiClass().getClassName(), ccCiClassInfo);
        }

        //查询出使用该业务组件的EBPM关系
        List<Long> queryUseCiIds = Collections.singletonList(rltClassMap.get(ebpmSY).getCiClass().getId());
        List<CcCiRltInfo> rltUseList = this.getRltByRltClassId(queryUseCiIds, null, Collections.singletonList(ciInfo.getCi().getId()), libType);
        //取出任务ci并排序
        List<CcCiInfo> sortTaskInfo = rltUseList.stream().map(CcCiRltInfo::getSourceCiInfo).sorted(Comparator.comparing(o -> o.getAttrs().get("ID"))).collect(Collectors.toList());
        //"EBPM-操作-C","EBPM-操作-R","EBPM-操作-U"
        Long cCiClassId = rltClassMap.get(ebpmCZC).getCiClass().getId();
        Long rCiClassId = rltClassMap.get(ebpmCZR).getCiClass().getId();
        Long uCiClassId = rltClassMap.get(ebpmCZU).getCiClass().getId();
        //取出全部CRU操作关系数据
        List<Long> queryCRUCiIds = Arrays.asList(cCiClassId,rCiClassId,uCiClassId);
        List<Long> sourceCiIds = sortTaskInfo.stream().map(ccCiInfo -> ccCiInfo.getCi().getId()).collect(Collectors.toList());
        List<CcCiRltInfo> cruRltInfoList = this.getRltByRltClassId(queryCRUCiIds, sourceCiIds, null, libType);

        //取出全部业务信息对象并排序
        List<CcCiInfo> sortBusInfo = cruRltInfoList.stream().map(ccCiRltInfo -> ccCiRltInfo.getTargetCiInfo()).distinct().collect(Collectors.toList());
        sortBusInfo.sort(Comparator.comparing(o -> o.getAttrs().get("业务信息ID")));

        //分别取出C、R、U数据组装Map<sourceCiCode+targetCiCode,CcCiRltInfo>
        Map<String, CcCiRltInfo> CMap = new HashMap<>();
        Map<String, CcCiRltInfo> RMap = new HashMap<>();
        Map<String, CcCiRltInfo> UMap = new HashMap<>();

        for(CcCiRltInfo ccCiRltInfo : cruRltInfoList){
            Long classId = ccCiRltInfo.getCiRlt().getClassId();
            String ciTag = ccCiRltInfo.getSourceCiInfo().getCi().getId()+""+ccCiRltInfo.getTargetCiInfo().getCi().getId();
            if(cCiClassId.equals(classId)){
                CMap.put(ciTag, ccCiRltInfo);
            }else if(rCiClassId.equals(classId)){
                RMap.put(ciTag, ccCiRltInfo);
            }else if(uCiClassId.equals(classId)){
                UMap.put(ciTag, ccCiRltInfo);
            }
        }

        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> resultList = new ArrayList<>();
        List<String> busNameList = sortBusInfo.stream().map(ccCiInfo -> ccCiInfo.getAttrs().get("业务信息名称")).collect(Collectors.toList());
        result.put("业务信息", busNameList);
        for(CcCiInfo ccCiInfo : sortTaskInfo){
            Map<String, Object> taskMap = new HashMap<>();
            Map<String, Object> busMap = new HashMap<>();
            for(CcCiInfo busCcCiInfo : sortBusInfo){
                List<String> busList = new ArrayList<>();
                String tagId = ccCiInfo.getCi().getId()+""+busCcCiInfo.getCi().getId();
                //c
                String strC = BinaryUtils.isEmpty(CMap.get(tagId))?"" : "C";
                busList.add(strC);
                //u
                String strU = BinaryUtils.isEmpty(UMap.get(tagId))?"" : "U";
                busList.add(strU);
                //r
                String strR = BinaryUtils.isEmpty(RMap.get(tagId))?"" : "R";
                busList.add(strR);
                busMap.put(busCcCiInfo.getAttrs().get("业务信息名称"), busList);
            }
            taskMap.put(ccCiInfo.getAttrs().get("名称"), busMap);
            resultList.add(taskMap);
        }
        result.put("任务",resultList);

        return result;
    }

    @Override
    public List<BizNodeDto> getBusinessTree(LibType libType) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        String classCode = bmConfigSvc.getConfigType(Env.BUS_TREE_NAME);
        if(BinaryUtils.isEmpty(classCode)){
            classCode = BUS_TREE_NAME;
        }
        CcCiClassInfo ciClass = ciClassApiSvc.getCIClassByCodes(classCode);
        if(BinaryUtils.isEmpty(ciClass) || BinaryUtils.isEmpty(ciClass.getCiClass())){
            throw new ServerException("未查询到分类【"+classCode+"】,请联系管理员!");
        }
        List<String> proNameList = ciClass.getAttrDefs().stream().map(CcCiAttrDef::getProName).collect(Collectors.toList());
        if(!proNameList.contains(ATTR_LEVEL) || !proNameList.contains(ATTR_NAME)){
            throw new ServerException("对象分类【"+classCode+"】配置有误,请联系管理员!");
        }
        CCcCi cdt = new CCcCi();
        cdt.setClassId(ciClass.getCiClass().getId());
        List<CcCiInfo> bizCiList = iciSwitchSvc.queryCiInfoList(domainId, cdt, null, true, true, libType);
        Map<String, Set<CcCiInfo>> abilityMap = getBizAbilityMap(bizCiList);
        // 第一层节点
        List<BizNodeDto> rootNodeList = new LinkedList<>();
        for(CcCiInfo bizCi: bizCiList){
            BizNodeDto itemNode = new BizNodeDto();
            itemNode.setName(bizCi.getAttrs().get(ATTR_NAME));
            itemNode.setCiInfo(bizCi);
            String ciLevel = bizCi.getAttrs().get(ATTR_LEVEL);
            if(ciLevel.equalsIgnoreCase("L1")){
                rootNodeList.add(itemNode);
            }
        }
        if (!rootNodeList.isEmpty()) {
            // 按名称排序
            rootNodeList.sort(Comparator.comparing(BizNodeDto::getName));
        }
        // 构建业务能力树
        rootNodeList.forEach((rootNode)->{
            findChild(rootNode, abilityMap);
        });
        // 第一层级按编号从小到大排序
        rootNodeList.sort(Comparator.comparing(o -> o.getCiInfo().getAttrs().get("编号")));
        return rootNodeList;
    }

    @Override
    public String queryPanoramaId(String codeName, LibType libType) {
        ESCISearchBean queryBean = getAppCISearchInfo("数据字典");
        if (CollectionUtils.isEmpty(queryBean.getClassIds())) {
            return null;
        }
        ESAttrBean codeTypeAttr = new ESAttrBean();
        codeTypeAttr.setKey("CODE_TYPE");
        codeTypeAttr.setValue("PANORAMA_DIAGRAM");
        codeTypeAttr.setOptType(1);
        ESAttrBean codeNameAttr = new ESAttrBean();
        codeNameAttr.setKey("CODE_NAME");
        codeNameAttr.setValue(codeName);
        codeNameAttr.setOptType(1);
        queryBean.setAndAttrs(Arrays.asList(codeTypeAttr, codeNameAttr));
        Page<ESCIInfo> esciInfoPage = iciSwitchSvc.searchESCIByBean(queryBean, libType);
        List<ESCIInfo> data = esciInfoPage.getData();
        String id = null;
        if(!org.springframework.util.CollectionUtils.isEmpty(data)){
            id = data.get(0).getAttrs().get("CODE_VALUE").toString();
        }
        return id;
    }

    @Override
    public List<ESDiagram> selectPanoramicWallDiagram(LibType libType, String body) {
        JSONObject jsonObj = JSONObject.parseObject(body);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (jsonObj.get("name") != null && !jsonObj.get("name").toString().isEmpty()) {
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("name.keyword","*"+jsonObj.get("name").toString().trim()+"*"));
        }

        boolQueryBuilder.must(QueryBuilders.termQuery("status",1));
        boolQueryBuilder.must(QueryBuilders.termQuery("dataStatus",1));
        boolQueryBuilder.must(QueryBuilders.termQuery("isOpen",1));
        List<ESDiagram> result = esDiagramDao.getListByQuery(boolQueryBuilder);
        return result;
    }

    @Override
    public String saveOrUpdatePanoramaDict(JSONObject jsonObj, LibType libType) {
        ESCISearchBean queryBean = getAppCISearchInfo("数据字典");
        ESAttrBean codeTypeAttr = new ESAttrBean();
        codeTypeAttr.setKey("CODE_TYPE");
        codeTypeAttr.setValue("PANORAMA_DIAGRAM");
        codeTypeAttr.setOptType(1);
        ESAttrBean codeNameAttr = new ESAttrBean();
        codeNameAttr.setKey("CODE_NAME");
        codeNameAttr.setValue(jsonObj.get("codeName"));
        codeNameAttr.setOptType(1);
        queryBean.setAndAttrs(Arrays.asList(codeTypeAttr, codeNameAttr));
        Page<ESCIInfo> esciInfoPage = iciSwitchSvc.searchESCIByBean(queryBean, libType);
        List<ESCIInfo> data = esciInfoPage.getData();
        if (data.isEmpty()) {
            return "修改字典中全局墙数据失败！";
        }else{
            ESCIInfo esciInfo = data.get(0);
            esciInfo.getAttrs().put("CODE_VALUE",jsonObj.get("diagramId"));
            CcCiInfo ciInfo = commSvc.tranCcCiInfo(esciInfo, false);
            iciSwitchSvc.saveOrUpdateCI(ciInfo,libType);
            return esciInfo.getCiCode();
        }
    }

    private ESCISearchBean getAppCISearchInfo(String className){
        ESCISearchBean queryBean = new ESCISearchBean();
        queryBean.setDomainId(1L);
        queryBean.setPageSize(2000);
        CCcCiClass cCcCiClass = new CCcCiClass();
        cCcCiClass.setClassNameEqual(className);
        List<CcCiClassInfo> ccCiClassInfos = iciClassApiSvc.queryClassByCdt(cCcCiClass);
        // 非空校验
        if (CollectionUtils.isEmpty(ccCiClassInfos)) {
            return queryBean;
        }
        Long app = ccCiClassInfos.get(0).getCiClass().getId();

        CcCiClassInfo ccCiClassInfo = iciClassApiSvc.queryClassInfoById(app);
        queryBean.setClassIds(Collections.singletonList(ccCiClassInfo.getCiClass().getId()));
        return queryBean;
    }

    /**
     * 获取业务能力Map
     * @param bizCiList 全部的业务能力
     *
     * @return Map<业务能力名称, 属于此业务能力的子业务能力CI集合>
     */
    private Map<String, Set<CcCiInfo>> getBizAbilityMap(List<CcCiInfo> bizCiList){
        Map<String, Set<CcCiInfo>> abilityMap = new HashMap<>();
        for(CcCiInfo itemCi: bizCiList){
            if(itemCi.getAttrs().containsKey(ATTR_ABILITY)) {
                String parentAbility = itemCi.getAttrs().get(ATTR_ABILITY);
                abilityMap.computeIfAbsent(parentAbility, key -> new HashSet<>());
                abilityMap.get(parentAbility).add(itemCi);
            }
        }
        return abilityMap;
    }

    /**
     * 获取指定业务系统的子业务系统
     *
     * @param bizNode 业务系统节点
     * @param abilityMap 所有业务系统列表
     */
    private void findChild(BizNodeDto bizNode, Map<String, Set<CcCiInfo>> abilityMap) {
        String parentNodeName = bizNode.getName();
        String parentNodeLevel = bizNode.getCiInfo().getAttrs().get(ATTR_LEVEL);
        Integer parentLenvel = Integer.parseInt(parentNodeLevel.split("L")[1]);
        // 如果业务能力包含子业务能力
        if (abilityMap.containsKey(parentNodeName)) {
            List<BizNodeDto> childList = new ArrayList<>();
            for (CcCiInfo itemCi : abilityMap.get(parentNodeName)) {
                if (itemCi.getAttrs().containsKey(ATTR_ABILITY)) {
                    String parentAbility = itemCi.getAttrs().get(ATTR_ABILITY);
                    String level = itemCi.getAttrs().get(ATTR_LEVEL);
                    Integer childLevel = Integer.parseInt(level.split("L")[1]);
                    // 如果系统的所属能力为给定CI的名称并且两个系统不在同一层级, 则认为此系统为给定CI的子业务系统
                    if (parentAbility.equalsIgnoreCase(parentNodeName) && (childLevel==parentLenvel+1)) {
                        BizNodeDto childNode = new BizNodeDto();
                        childNode.setCiInfo(itemCi);
                        childNode.setName(itemCi.getAttrs().get(ATTR_NAME));
                        childList.add(childNode);
                    }
                }
            }
            if (childList.size() > 0) {
                // 按名称排序
                childList.sort(Comparator.comparing(BizNodeDto::getName));
            }
            bizNode.setChildren(childList);
            // 递归查询
            bizNode.getChildren().forEach((nextNode) -> {
                findChild(nextNode, abilityMap);
            });
        }
    }

    @Override
    public List<PanoramaCiInfoVo> businessTree(Long dataSetId){
        JSONObject dataSet = dataSetApiSvc.findDataSetById(dataSetId);
        if(dataSet == null){
            throw new ServerException("配置信息已过时,请更新矩阵分析配置!");
        }
        if(dataSet.getInteger("shareLevel").equals(OperateType.Invisible.getCode())){
            throw MessageException.i18n("DCV_BS_OBJ_DATASET_NO_PERMISSION");
        }
        String nodeJson = dataSet.getString("nodes");
        if(BinaryUtils.isEmpty(nodeJson)){
            throw MessageException.i18n("DCV_BS_OBJ_DATASET_WITHOUT_NODE!");
        }
        List<RelationRuleNode> nodes = JSON.parseArray(nodeJson, RelationRuleNode.class);
        List<RelationRuleLine> lines = new ArrayList<>();
        String linkJson = dataSet.getString("lines");
        if(!BinaryUtils.isEmpty(linkJson)){
            lines = JSON.parseArray(linkJson, RelationRuleLine.class);
        }
        Set<Long> ciClassIds = new HashSet<>();
        Map<Long, RelationRuleNode> pageNodeMap = new HashMap<>();
        for (RelationRuleNode node : nodes) {
            ciClassIds.add(node.getClassId());
            pageNodeMap.put(node.getPageNodeId(), node);
        }
        RelationRuleNode rootNode = pageNodeMap.get(dataSet.getLong("pageNodeId"));
        Map<Long, List<CiRltQueryVo>> startNodeGroup = new HashMap<>();
        Set<Long> rltClassIds = new HashSet<>();
        for (RelationRuleLine line : lines) {
            RelationRuleNode startNode = pageNodeMap.get(line.getNodeStartId());
            RelationRuleNode endNode = pageNodeMap.get(line.getNodeEndId());
            CiRltQueryVo queryVo = EamUtil.copy(line, CiRltQueryVo.class);
            //判断是否存在"被",存在则需要做反方向关系查询
            if(line.getDirection()){
                queryVo.setSourceClassId(startNode.getClassId());
                queryVo.setTargetClassId(endNode.getClassId());
            }else{
                queryVo.setSourceClassId(endNode.getClassId());
                queryVo.setTargetClassId(startNode.getClassId());
            }
            rltClassIds.add(line.getClassId());
            startNodeGroup.computeIfAbsent(line.getNodeStartId(), key -> new ArrayList<>()).add(queryVo);
        }
        List<PanoramaCiInfoVo> result = new ArrayList<>();
        //查询所有ci数据
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByClassIds(new ArrayList<>(ciClassIds), null, LibType.DESIGN);
        if(CollectionUtils.isEmpty(ciList)){
            return result;
        }
        Map<String, ESCIInfo> ciMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, e -> e, (k1, k2) -> k2));
        Map<Long, List<ESCIInfo>> ciGroup = ciList.stream().collect(Collectors.groupingBy(CcCi::getClassId));
        List<ESCIInfo> rootCiList = ciGroup.get(rootNode.getClassId());
        if(CollectionUtils.isEmpty(rootCiList)){
            return result;
        }

        //查询所有关系数据
        List<ESCIRltInfo> rltInfoList = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(rltClassIds)){
            rltInfoList = ciRltSvc.searchRlt(rltClassIds, ciClassIds, ciClassIds, LibType.DESIGN);
        }
        Map<String, List<ESCIRltInfo>> rltGroup = rltInfoList.stream().collect(Collectors.groupingBy(e -> e.getSourceClassId() + "_" + e.getClassId() + "_" + e.getTargetClassId()));

        for (ESCIInfo each : rootCiList) {
            String name = each.getCiLabel().replaceAll("[\\[\\]\\\\\"]", "");
            if(BinaryUtils.isEmpty(name)){
                name = each.getCiPrimaryKey().replaceAll("[\\[\\]\\\\\"]", "");
            }
            PanoramaCiInfoVo ciInfo = new PanoramaCiInfoVo(each.getId(), name, each.getCiCode(), each.getClassId(), null);
            result.add(ciInfo);
        }
        Map<Long, List<PanoramaCiInfoVo>> nodeMap = new HashMap<>();
        nodeMap.put(rootNode.getPageNodeId(), result);
        //一个节点不走第二次
        this.setChildren(nodeMap, startNodeGroup, rltGroup, ciMap, new HashSet<>());
        return result;
    }

    /**
     * 填充子集
     */
    private void setChildren(Map<Long, List<PanoramaCiInfoVo>> nodeMap, Map<Long, List<CiRltQueryVo>> startNodeGroup,
                             Map<String, List<ESCIRltInfo>> rltGroup, Map<String, ESCIInfo> ciMap, Set<Long> distinct){
        if(nodeMap.isEmpty()){
            return;
        }
        Map<Long, List<PanoramaCiInfoVo>> childNodeMap = new HashMap<>();
        for (Map.Entry<Long, List<PanoramaCiInfoVo>> entry : nodeMap.entrySet()) {
            //查询所有数据超市末端节点
            List<CiRltQueryVo> linkList = startNodeGroup.get(entry.getKey());
            if(CollectionUtils.isEmpty(linkList) || distinct.contains(entry.getKey())){
                //没有关系线或走入循环节点，结束
                continue;
            }
            Map<String, PanoramaCiInfoVo> parentMap = entry.getValue().stream().collect(Collectors.toMap(CiSimpleInfoVo::getCiCode, e -> e, (k1, k2) -> k2));
            //有末端节点，取出所有关系数据
            for (CiRltQueryVo link : linkList) {
                List<ESCIRltInfo> rltInfoList = rltGroup.getOrDefault(link.getRltCode(), new ArrayList<>());
                for (ESCIRltInfo rltInfo : rltInfoList) {
                    //判断数据超市关系线方向
                    String parentCode = link.getDirection()?rltInfo.getSourceCiCode():rltInfo.getTargetCiCode();
                    String childCode = link.getDirection()?rltInfo.getTargetCiCode():rltInfo.getSourceCiCode();
                    ESCIInfo ciInfo = ciMap.get(childCode);
                    if(!parentMap.containsKey(parentCode) || ciInfo == null){
                        continue;
                    }
                    String name = ciInfo.getCiLabel().replaceAll("[\\[\\]\\\\\"]", "");
                    if(BinaryUtils.isEmpty(name)){
                        name = ciInfo.getCiPrimaryKey().replaceAll("[\\[\\]\\\\\"]", "");
                    }
                    PanoramaCiInfoVo child = new PanoramaCiInfoVo(ciInfo.getId(), name, ciInfo.getCiCode(), ciInfo.getClassId(), null);
                    //填充子集ci
                    parentMap.get(parentCode).getChildren().add(child);
                    //收集子集ci进入下一次轮回
                    childNodeMap.computeIfAbsent(link.getNodeEndId(), key -> new ArrayList<>()).add(child);
                }
            }
            distinct.add(entry.getKey());
        }
        this.setChildren(childNodeMap, startNodeGroup, rltGroup, ciMap, distinct);
    }

    @Override
    public List<CcCiInfo> businessCi(Long dataSetId) {
        List<PanoramaCiInfoVo> panoramaList = this.businessTree(dataSetId);
        List<String> ciCodes = new ArrayList<>();
        this.getChildCodes(panoramaList, ciCodes);
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        CCcCi query = new CCcCi();
        query.setCiCodes(ciCodes.toArray(new String[]{}));
        return iciSwitchSvc.queryCiInfoList(domainId, query, "", true, true, LibType.DESIGN);
    }

    private void getChildCodes(List<PanoramaCiInfoVo> panoramaList, List<String> ciCodes){
        if(CollectionUtils.isEmpty(panoramaList)){
            return;
        }
        List<PanoramaCiInfoVo> childList = new ArrayList<>();
        for (PanoramaCiInfoVo each : panoramaList) {
            ciCodes.add(each.getCiCode());
            childList.addAll(each.getChildren());
        }
        this.getChildCodes(childList, ciCodes);
    }
}
